﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Web.Script.Serialization;
using David.Framework.Core.Helper;

namespace David.Framework.Core.Extensions
{
    public static class StringExtension
    {
        private static readonly Regex WebUrlExpression = new Regex(@"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", RegexOptions.Singleline | RegexOptions.Compiled);
        private static readonly Regex EmailExpression = new Regex(@"^([0-9a-zA-Z-_.])+@[0-9a-zA-Z-.]+$", RegexOptions.Singleline | RegexOptions.Compiled);
        private static readonly Regex StripHTMLExpression = new Regex("<\\S[^><]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
        private static readonly Regex UserNameExpression = new Regex("^[a-zA-Z0-9](([a-zA-Z0-9_]{0,28})?[a-zA-Z0-9])?$", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
        private static readonly Regex PasswordExpression = new Regex("^.{6,30}$", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
        /*
                private static readonly char[] IllegalUrlCharacters = new[] { ';', '/', '\\', '?', ':', '@', '&', '=', '+', '$', ',', '<', '>', '#', '%', '.', '!', '*', '\'', '"', '(', ')', '[', ']', '{', '}', '|', '^', '`', '~', '–', '‘', '’', '“', '”', '»', '«' };
        */

        [DebuggerStepThrough]
        public static bool IsPassword(this string target)
        {
            return !string.IsNullOrEmpty(target) && PasswordExpression.IsMatch(target);
        }

        [DebuggerStepThrough]
        public static bool IsUserName(this string target)
        {
            return !string.IsNullOrEmpty(target) && UserNameExpression.IsMatch(target);
        }

        [DebuggerStepThrough]
        public static bool IsNullOrEmpty(this string target)
        {
            return string.IsNullOrEmpty(target);
        }

        [DebuggerStepThrough]
        public static bool IsWebUrl(this string target)
        {
            return !string.IsNullOrEmpty(target) && WebUrlExpression.IsMatch(target);
        }

        [DebuggerStepThrough]
        public static bool IsEmail(this string target)
        {
            return !string.IsNullOrEmpty(target) && EmailExpression.IsMatch(target);
        }

        [DebuggerStepThrough]
        public static string NullSafe(this string target)
        {
            return (target ?? string.Empty).Trim();
        }

        [DebuggerStepThrough]
        public static string FormatWith(this string target, params object[] args)
        {
            Check.Argument.IsNotEmpty(target, "target");

            return string.Format(CultureInfo.CurrentCulture, target, args);
        }

        [DebuggerStepThrough]
        public static string WrapAt(this string target, int index)
        {
            const int dotCount = 3;

            Check.Argument.IsNotEmpty(target, "target");
            Check.Argument.IsNotNegativeOrZero(index, "index");

            return (target.Length <= index) ? target : string.Concat(target.Substring(0, index - dotCount), new string('.', dotCount));
        }

        [DebuggerStepThrough]
        public static string StripHtml(this string target)
        {
            return StripHTMLExpression.Replace(target, string.Empty);
        }

        [DebuggerStepThrough]
        public static Guid ToGuid(this string target)
        {
            Guid result = Guid.Empty;

            if ((!string.IsNullOrEmpty(target)) && (target.Trim().Length == 22))
            {
                string encoded = string.Concat(target.Trim().Replace("-", "+").Replace("_", "/"), "==");

                try
                {
                    byte[] base64 = Convert.FromBase64String(encoded);

                    result = new Guid(base64);
                }
                catch (FormatException)
                {
                }
            }

            return result;
        }

        [DebuggerStepThrough]
        public static T ToEnum<T>(this string target, T defaultValue) where T : IComparable, IFormattable
        {
            T convertedValue = defaultValue;

            if (!string.IsNullOrEmpty(target))
            {
                try
                {
                    convertedValue = (T)Enum.Parse(typeof(T), target.Trim(), true);
                }
                catch (ArgumentException)
                {
                }
            }

            return convertedValue;
        }

        [DebuggerStepThrough]
        public static string UrlEncode(this string target)
        {
            return HttpUtility.UrlEncode(target);
        }

        [DebuggerStepThrough]
        public static string UrlDecode(this string target)
        {
            return HttpUtility.UrlDecode(target);
        }

        [DebuggerStepThrough]
        public static string AttributeEncode(this string target)
        {
            return HttpUtility.HtmlAttributeEncode(target);
        }

        [DebuggerStepThrough]
        public static string HtmlEncode(this string target)
        {
            return HttpUtility.HtmlEncode(target);
        }

        [DebuggerStepThrough]
        public static string HtmlDecode(this string target)
        {
            return HttpUtility.HtmlDecode(target);
        }

        [DebuggerStepThrough]
        public static string Replace(this string target, ICollection<string> oldValues, string newValue)
        {
            oldValues.ForEach(oldValue => target = target.Replace(oldValue, newValue));
            return target;
        }
        [DebuggerStepThrough]
        public static bool CheckSkuValueIsNull(this string target)
        {
            return target.Trim() == "%20" || target.Trim() == "undefined" || string.IsNullOrWhiteSpace(target);
        }
        [DebuggerStepThrough]
        public static string SubString(this string target, int length)
        {
            return SubString(target, length, 0);
        }

        [DebuggerStepThrough]
        public static string SubString(this string target, int length, int pointNum)
        {
            Check.Argument.IsNotNegativeOrZero(length, "length");
            Check.Argument.IsNotNegative(pointNum, "pointNum");

            string result = "";

            if (target.Length > length)
            {
                result = target.Substring(0, length);
            }

            for (int i = 0; i < pointNum; i++)
            {
                result += ".";
            }

            return result;
        }

        /// <summary>
        /// 过滤除了数字和字母以外的字符
        /// </summary>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static string GetNumAndLetter(this string target)
        {
            var r = new Regex("[a-zA-Z0-9]+");

            var match = r.Matches(target);

            return match.Cast<object>().Aggregate("", (current, s) => current + s);
        }

        /// <summary>
        /// "/"替换成"+",并过滤除了数字字母和"+"以外的字符
        /// </summary>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static string GetNumLetterSlash(this string target)
        {
            target = target.Replace("/", "+");

            var r = new Regex(@"[a-zA-Z0-9\+]+");

            var match = r.Matches(target);

            return match.Cast<object>().Aggregate("", (current, s) => current + s);
        }

        /// <summary>
        /// 如果为null返回空，其他返回本身
        /// </summary>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static string ToStringOrEmpty(this string target)
        {
            if (string.IsNullOrEmpty(target))
            {
                return string.Empty;
            }

            return target;
        }

        /// <summary>
        /// 任意禁止转换
        /// </summary>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static decimal ToDecimal(this string target)
        {
            const string h = "0123456789abcdefghijklmnopqrstuvwxyz";

            var strarr = target.ToCharArray().OrderByDescending(a => a).ToArray();

            decimal result = 0;

            for (int i = 0; i < strarr.Count(); i++)
            {
                var index = h.IndexOf(strarr[i]);

                var temp = (decimal)Math.Pow(36, i);

                result += (index * temp);
            }

            return result;
        }

        /// <summary>
        /// 将指定的 JSON 字符串转换为 T 类型的对象。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="target"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static T Deserialize<T>(this string target) where T : class
        {
            try
            {
                var serializer = new JavaScriptSerializer();
                return serializer.Deserialize<T>(target);
            }
            catch (Exception)
            {
                return null;
            }

        }

        /// <summary>
        /// 将 JSON 格式字符串转换为指定类型的对象。
        /// </summary>
        /// <param name="target"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static object Deserialize(this string target, Type type)
        {
            var serializer = new JavaScriptSerializer();
            return serializer.Deserialize(target, type);
        }

        /// <summary>
        /// 将指定的 JSON 字符串转换为对象图。
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static object DeserializeObject(this string target)
        {
            var serializer = new JavaScriptSerializer();
            return serializer.DeserializeObject(target);
        }

        /// <summary>
        /// 全角转半角
        /// </summary>
        /// <param name="target"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static string ToDBC(this string target)
        {
            char[] c = target.ToCharArray();
            for (int i = 0; i < c.Length; i++)
            {
                if (c[i] == 12288)
                {
                    c[i] = (char)32;
                    continue;
                }
                if (c[i] > 65280 && c[i] < 65375)
                    c[i] = (char)(c[i] - 65248);
            }
            return new string(c);
        }
    }
}
